/*****************************************
Filename  : sw_uart.h
Author    : Prashant Mehta
Date	  : 26-06-2009
******************************************/
/*                   
*******************************************************************************************************************
*  Header file for SOFTWARE UART using timer 0.									  *
*  Timer 0 is set to 3 times the required baud rate because data sampling has to be done 3 times per bit	  *
*  5,6,7,8 bit Tx and Rx possible by this s/w UART								  *
*  1 or 2 Stop bits as pre-defined by user									  *
*  Possible to use parity bit (even or odd) along with data frame						  *
*  parity_error flag set if parity error									  *
*  Use sw_uart_config.h to configure Software UART. Dont make any changes here.  				  *
*  sw_uart_init()              -- Function to initialize Software UART						  *
*  sw_UART_Transmit_char(char) -- Function for Transmitting character						  *
*  sw_UART_Receive_char()      -- Function for Receiving character						  *
*******************************************************************************************************************
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "sw_uart_config.h"

#define TRUE 1
#define FALSE 0

#define IN_BUF_SIZE		256


/********************************************   Function Prototypes   ************************************************/
void set_tx_pin_high(void);	
void set_tx_pin_low(void);
void timer0_init(void);
void sw_uart_init(void);
static void sw_uart_io_init(void);
void sw_UART_Transmit_char(char ch);
void sw_UART_Transmit_string(char* ch);
void sw_UART_Transmit_double(double data);
char sw_UART_Receive_char( void );
int get_rx_pin_status(void);
void softuart_turn_rx_on(void);
void flush_input_buffer(void);
void frame_calc(unsigned char);
/*********************************************************************************************************************/

/******************************************* Software UART variables  **********************************************/
volatile static unsigned char		inbuf[IN_BUF_SIZE];
volatile static unsigned char		qin = 0;
volatile static unsigned char		qout = 0;


volatile unsigned static char 			flag_tx_ready;
volatile unsigned static char 			timer_tx_ctr;
volatile unsigned static char 			bits_left_in_tx;
volatile unsigned static char 			tx_num_of_bits;
volatile unsigned static short			internal_tx_buffer;
volatile unsigned static char			user_tx_buffer;
volatile unsigned static char			num_of_databits;
volatile unsigned static char			parity_error;

volatile static unsigned char    		flag_rx_off;
volatile static unsigned char   		flag_rx_ready;
volatile static unsigned char			tx_num_of_ones=0;
volatile static unsigned char			rx_num_of_ones=0;
volatile static unsigned int			frame;
volatile static unsigned int			rx_parity_bit;
/*********************************************************************************************************************/

/* Function to initialize uart PB7 as TX pin and PG3 as RX pin.
   Also initialize timer 0 for delay of 3 times the baud rate*/
void sw_uart_init(void)
{
	sw_uart_io_init();

	/* Condition to double the baud rate if U2X2 = 1 */
	if(U2X2 == 1)
	{
		baudrate = 2 * (BAUD_RATE);
	}
	else baudrate = BAUD_RATE;
	/*************************************************/

	flag_tx_ready = FALSE;
	tx_num_of_bits = NUM_OF_DATABITS+NUM_OF_STOPBITS+1+PARITY_BIT;
	set_tx_pin_high();	
	timer0_init();					// start timer 0
}
/*********************************************************************************************************************/


/******************************************  Timer ISR on oveflow  ***********************************************/

ISR (TIMER0_COMPA_vect)					// Interrupt on compare match
{
	char mask;
	static unsigned char flag_rx_waiting_for_stop_bit = FALSE;
	static unsigned char rx_mask;
	
	static char timer_rx_ctr;	// Counter for sampling at reciption
	static char bits_left_in_rx;
	static unsigned char internal_rx_buffer;
	
	char start_bit, flag_in;
	/***                 Transmitter section                 ***/
	if(flag_tx_ready)
	{

		if(--timer_tx_ctr<=0)
		{

			mask=internal_tx_buffer&1;
			internal_tx_buffer>>=1;
			if(mask)
				{
					set_tx_pin_high();
				
				}
			else
				{
					set_tx_pin_low();
				}
			timer_tx_ctr = 3;
			if ( --bits_left_in_tx<=0 )
				{
				flag_tx_ready = FALSE;
				}
		}
	}
	/***                Reciever section                  ***/
	if ( flag_rx_off == FALSE )
	{
		if ( flag_rx_waiting_for_stop_bit ) 		// for stop bit
		{
			if ( --timer_rx_ctr <= 0 ) 
			{
				if(get_rx_pin_status())
				{
					flag_rx_waiting_for_stop_bit = FALSE;
					flag_rx_ready = FALSE;
					inbuf[qin] = internal_rx_buffer;
					if ( ++qin>=IN_BUF_SIZE )
						{
						qin = 0;

						}

				}
			}
		}
		else
		{  
			if ( flag_rx_ready == FALSE )
			{
				start_bit = get_rx_pin_status();
								// test for start bit
				if ( start_bit == 0 )
				{
					flag_rx_ready = TRUE;
					internal_rx_buffer= 0;
					timer_rx_ctr= 4;	// here 4 so that bit is sampled at middle
					bits_left_in_rx = NUM_OF_DATABITS+PARITY_BIT;
					rx_mask=1;
				}
			}
			else
			{  // rx_busy
				if ( --timer_rx_ctr <= 0 )
					{
					// rcv
					timer_rx_ctr = 3;
					flag_in = get_rx_pin_status();
					if(!PARITY_BIT)	// No parity bit
					{
						if ( flag_in )				// OR with 1 according to rx pin status
						{
						internal_rx_buffer |= rx_mask;
						}
						rx_mask <<= 1;				// left shift for each bit
					}
					else		// For parity bit
					{
						if(bits_left_in_rx!=1)
						{
							if ( flag_in )				// OR with 1 according to rx pin status
							{
							internal_rx_buffer |= rx_mask;
							rx_num_of_ones++;
							}
							rx_mask <<= 1;				// left shift for each bit
						}
						else
						{
							if(PARITY)	// For ODD parity
							{
								if((rx_num_of_ones%2)==0)	// Recieved even number of ones
								{								
									if(flag_in==0) parity_error=1;
								}
								else           			// Recieved odd number of ones
									if(flag_in==1) parity_error=1;
							}
							else		// Even parity
							{
								if((rx_num_of_ones%2)!=0)	// Recieved odd number of ones
								{								
									if(flag_in==0) parity_error=1;
								}
								else           			// Recieved even number of ones
									if(flag_in==1) parity_error=1;
							}
						}
					}
					if ( --bits_left_in_rx <= 0 )
						{
							flag_rx_waiting_for_stop_bit = TRUE;	// now go for stop bit
					}
				}
			}
		}
	}
	//TCNT0=0xEF;
}
/***********************************************************************************************************************/

/*********************************** Function to transmit a character on Tx pin ****************************************/
void sw_UART_Transmit_char(char ch)
{
	while ( flag_tx_ready );
	user_tx_buffer = ch;
	timer_tx_ctr = 3;
	bits_left_in_tx = tx_num_of_bits;
	frame_calc(user_tx_buffer);
	internal_tx_buffer = (user_tx_buffer<<1)|frame;	
	flag_tx_ready = TRUE;
}

void sw_UART_Transmit_string(char* ch)
{
	for (int i = 0; i < strlen(ch); i++)
		sw_UART_Transmit_char(ch[i]);
}

void sw_UART_Transmit_double(double data)
{
	char buffer[15];
//	sprintf(buffer, "%lf", data);
	dtostrf(data, 5, 6, buffer);
	sw_UART_Transmit_string(buffer);
//	sw_UART_Transmit_string(printf("%f", data));
}
/*********************************************************************************************************************/

/********************************** Function to recieve a character from Rx pin ****************************************/
char sw_UART_Receive_char( void )
{
	char ch;
	softuart_turn_rx_on();
	while (qout==qin) //Wait till reception complete
	{
	}
	ch=inbuf[qout];	if ( ++qout>=IN_BUF_SIZE )
		{
		qout = 0;
		}
	return ch ;
}
/***********************************************************************************************************************/

/********************************* Function to get pin status from Rx pin i.e. PG3 *************************************/
int get_rx_pin_status(void)
{
	if ( UART_RXPIN  & ( 1 << UART_RXBIT ) )
	{return 1;}
	return 0;
}
/************************************************************************************************************************/

/***************************************** Function to turn receiption ON ***********************************************/
void softuart_turn_rx_on( void )
{
	flag_rx_off = FALSE;
}
/************************************************************************************************************************/

/************************************** Function to turn receiption OFF *************************************************/
void softuart_turn_rx_off( void )
{
	flag_rx_off = TRUE;
}
/************************************************************************************************************************/

/************************************ Function to initialise s/w uart Tx and Rx ports ***********************************/
static void sw_uart_io_init(void)
{
	// TX-Pin as output
	UART_TXDDR |=  ( 1 << UART_TXBIT );
	// RX-Pin as input
	UART_RXDDR &= ~( 1 << UART_RXBIT );
	// RX-pin pull-up
	UART_RXPORT |= (1  << UART_RXBIT );
}
/************************************************************************************************************************/

/****************************************** Function to set Tx pin(PB7) high ********************************************/
void set_tx_pin_high(void)
{
	UART_TXPORT |=  ( 1<< UART_TXBIT );
}
/***********************************************************************************************************************/

/***************************************  Function to set Tx pin(PB7) low  **********************************************/
void set_tx_pin_low(void)
{
 	UART_TXPORT &= ~( 1<<UART_TXBIT );
}
/***********************************************************************************************************************/

/*******************************  Function for clearing contents of input buffer **************************************/
void flush_input_buffer(void)
{
	qin = 0;
	qout = 0;
}
/***********************************************************************************************************************/

/***************************  Function for adding parity bit & stop bits into data frame *******************************/
void frame_calc(unsigned char ch)
{
	unsigned char temp=0,i;
	for(i=0;i<=NUM_OF_DATABITS;i++)
	{
		temp=ch&1;
		ch>>=1;
		if(temp==1)
			tx_num_of_ones++;
	}
	if(PARITY_BIT)	// For PARITY BIT
	{
		if((tx_num_of_ones%2)==0)	//EVEN number of ones
		{
			if(PARITY)			//  ODD PARITY
			{
				if (NUM_OF_STOPBITS==1)		// For 1 stop-bit
				{
					frame=0x600>>(8-NUM_OF_DATABITS);
				}
				else				// For 2 stop-bits
				{
					frame=0xe00>>(8-NUM_OF_DATABITS);
				}
			}
			else			//EVEN PARITY
			{
				if (NUM_OF_STOPBITS==1)		// For 1 stop-bit
				{
					frame=0x400>>(8-NUM_OF_DATABITS);
				}
				else				// For 2 stop-bits
				{
					frame=0xc00>>(8-NUM_OF_DATABITS);
				}
			}
		}
		else				// ODD Number of ones
		{
			if(!PARITY)		//  EVEN PARITY
			{
				if (NUM_OF_STOPBITS==1)		// For 1 stop-bit
				{
					frame=0x600>>(8-NUM_OF_DATABITS);
				}
				else				// For 2 stop-bits
				{
					frame=0xe00>>(8-NUM_OF_DATABITS);
				}
			}
			else			//ODD PARITY
			{
				if (NUM_OF_STOPBITS==1)		// For 1 stop-bit
				{
					frame=0x400>>(8-NUM_OF_DATABITS);
				}
				else				// For 2 stop-bits
				{
					frame=0xc00>>(8-NUM_OF_DATABITS);
				}
			}
		}
	}
	else			// No parity bit
	{
		if (NUM_OF_STOPBITS==1)		// For 1 stop-bit
		{
			frame=0x200>>(8-NUM_OF_DATABITS);
		}
		else				// For 2 stop-bits
		{
			frame=0x600>>(8-NUM_OF_DATABITS);
		}
	}
}
/***********************************************************************************************************************/
